Parte 09 - Introdução à Programas Criptografados

Acredite ou não, é possível realizar cálculos com dados criptografados. Em outras palavras, é possível executar um programa onde TODAS as variáveis estão criptografadas!

Neste tutorial, nós vamos falar sobre as ferramentas básicas de computação criptografada. Em particular, nós vamos focar em uma abordagem popular chamada Computação Multiparte Segura (i.e. Secure Multi-Party Computation ou SMPC). Nesta lição, você aprenderá como criar uma calculadora criptografada que pode executar cálculos com números criptografados.

Autores:

Tradução:

Referências:

Passo 1: Criptografia Usando Computação Multiparte Segura

SMPC é, à primeira vista, uma forma bastante estranha de "criptografia". Em vez de usar uma chave pública/privada para criptografar uma variável, cada valor é dividido em várias partes, cada uma das quais opera como uma chave privada. Normalmente, essas partes serão distribuídas entre 2 ou mais proprietários. Assim, para decifrar a variável, todos os proprietários devem concordar em permitir a sua descriptografia. Em essência, todos têm uma chave privada.

Encrypt()

Então, digamos que queríamos "encriptar" uma variável x, podíamos fazê-lo da seguinte forma.

A criptografia não utiliza números reais ou de ponto flutuante mas um espaço matemático chamado de integer quotient ring (anel quociente inteiro) que são basicamente os inteiros entre 0 e Q-1, onde Q é primo e "grande o suficiente" tal que o espaço contenha todos os números que nós usamos em nossos experimentos. Na prática, dado um valor x inteiro, fazemos x % Q (resto da divisão inteira) para que esteja contido no "anel". (É por isso que evitamos usar o número x > Q)


In [1]:
Q = 1234567891011

In [2]:
x = 25

In [3]:
import random

def encrypt(x):
    share_a = random.randint(-Q,Q) # Entenda share_a como parte A de x, idem para B, e C.
    share_b = random.randint(-Q,Q)
    share_c = (x - share_a - share_b) % Q
    return (share_a, share_b,  share_c)

In [4]:
encrypt(x)


Out[4]:
(-886845047876, 450224257262, 436620790639)

Como você pode ver aqui, nós dividimos nossa variável x em 3 partes diferentes, que poderiam ser enviadas para 3 diferentes proprietários de dados.

Decrypt()

Se quiséssemos decifrar essas três partes, poderíamos simplesmente juntá-las e pegar o módulo do resultado (mod Q)


In [5]:
def decrypt(*shares):
    return sum(shares) % Q

In [6]:
a,b,c = encrypt(25)

In [7]:
decrypt(a, b, c)


Out[7]:
25

É importante notar que se tentarmos decifrar com apenas duas partes, a descriptografia não funciona!


In [8]:
decrypt(a, b)


Out[8]:
678152675788

Assim, precisamos que todos os proprietários participem para decifrar o valor. É desta forma que as partes agem como chaves privadas, todas as quais devem estar presentes para decifrar um valor.

Passo 2: Aritmética Básica Usando SMPC

No entanto, a propriedade verdadeiramente extraordinária da Computação Multiparte Segura é a capacidade de realizar cálculos enquanto as variáveis ainda estão criptografadas. Vamos demonstrar uma simples adição abaixo.


In [9]:
x = encrypt(25)
y = encrypt(5)

In [10]:
def add(x, y):
    z = list()
    # o primeiro worker adiciona as suas partes
    z.append((x[0] + y[0]) % Q)
    
    # o segundo worker adiciona as suas partes
    z.append((x[1] + y[1]) % Q)
    
    # o terceiro worker adiciona as suas partes
    z.append((x[2] + y[2]) % Q)
    
    return z

In [11]:
decrypt(*add(x,y))


Out[11]:
30

Sucesso!!!

E aqui o temos! Se cada worker (separadamente) somar suas partes, então as partes resultantes serão decifradas para o valor correto (25 + 5 === 30).

Acontece que, existem protocolos SMPC que podem permitir este cálculo criptografado para as seguintes operações:

  • Adição (que acabamos de ver)
  • Multiplicação
  • Comparação

e usando estas três operações básicas, podemos fazer cálculos arbitrários!!!

Na próxima seção, vamos aprender como usar a biblioteca PySyft para realizar estas operações!

Passo 3: SMPC Usando PySyft

Nas seções anteriores, nós demostramos algumas intuições básicas em torno do SMPC e como o método deve funcionar. Contudo, na prática, não queremos ter de ser nós próprios a escrever à mão todas as operações primitivas ao escrever os nossos programas criptografados. Então, nesta seção vamos passar pelo básico de como fazer computação criptografada usando o PySyft. Em particular, vamos focar em como fazer as 3 operações básicas mencionados anteriormente: adição, multiplicação, e comparação.

Primeiro, precisamos criar alguns Workers Virtuais (esperamos que você já esteja familiarizado com os nossos tutoriais anteriores).


In [12]:
import torch
import syft as sy
hook = sy.TorchHook(torch)

bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
bill = sy.VirtualWorker(hook, id="bill")

Criptografia e Descriptografia Básica

Criptografar é tão simples como tomar qualquer tensor PySyft e chamar .share(). Descriptografar é tão simples como chamar .get() na variável compartilhada.


In [13]:
x = torch.tensor([25])

In [14]:
x


Out[14]:
tensor([25])

In [15]:
encrypted_x = x.share(bob, alice, bill)

In [16]:
encrypted_x.get()


Out[16]:
tensor([25])

Considerações sobre os Valores Criptografados

Se olharmos mais de perto para os workers Alice, Bob e Bill, podemos ver as partes que são criadas em cada um deles!


In [17]:
bob._objects


Out[17]:
{}

In [18]:
x = torch.tensor([25]).share(bob, alice, bill)

In [19]:
# Parte de Bob
bobs_share = list(bob._objects.values())[0]
bobs_share


Out[19]:
tensor([-1394082603729046503])

In [20]:
# Parte de Alice
alices_share = list(alice._objects.values())[0]
alices_share


Out[20]:
tensor([7371401829207945737])

In [21]:
# Parte de Bill
bills_share = list(bill._objects.values())[0]
bills_share


Out[21]:
tensor([-5977319225478899209])

E se quiséssemos, poderíamos decifrar esses valores usando a mesma abordagem de que falamos anteriormente!!!


In [22]:
(bobs_share + alices_share + bills_share)


Out[22]:
tensor([25])

As you can see, when we called .share() it simply split the value into 3 shares and sent one share to each of the parties!

Como você pode ver, quando chamamos .share() simplesmente dividimos o valor em 3 partes e o enviamos para cada um dos workers !

Aritmética Criptografada

E agora você vê que nós podemos fazer aritmética sobre os valores subjacentes! A API é construída para que possamos simplesmente executar a aritmética como faríamos com os tensores PyTorch normais.


In [23]:
x = torch.tensor([25]).share(bob,alice)
y = torch.tensor([5]).share(bob,alice)

In [24]:
z = x + y
z.get()


Out[24]:
tensor([30])

In [25]:
z = x - y
z.get()


Out[25]:
tensor([20])

Multiplicação Criptografada

Para a multiplicação, precisamos de uma parte adicional que seja responsável pela geração consistente de números aleatórios (e que não colida com nenhuma das outras partes). Nós chamamos essa terceira parte de "provedor de criptografia". Para todos os propósitos, o provedor de criptografia é apenas um "VirtualWorker" adicional, mas é importante reconhecer que o provedor de criptografia não é um "dono/proprietário", pois ele não possui partes, mas é alguém que precisa ser confiável para não entrar em conluio com nenhuma das partes existentes.


In [26]:
crypto_provider = sy.VirtualWorker(hook, id="crypto_provider")

In [27]:
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)

In [28]:
# multiplicação

z = x * y
z.get()


Out[28]:
tensor([125])

Você também pode fazer multiplicação matricial


In [29]:
x = torch.tensor([[1, 2],[3,4]]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([[2, 0],[0,2]]).share(bob,alice, crypto_provider=crypto_provider)

In [30]:
# multiplicação de matrizes

z = x.mm(y)
z.get()


Out[30]:
tensor([[2, 4],
        [6, 8]])

Comparação Criptografada

Também é possível fazer comparações privadas entre valores privados. Contamos aqui com o protocolo SecureNN , cujos detalhes podem ser encontrados aqui. O resultado da comparação é também um tensor privado compartilhado.


In [31]:
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)

In [32]:
z = x > y
z.get()


Out[32]:
tensor([1])

In [33]:
z = x <= y
z.get()


Out[33]:
tensor([0])

In [34]:
z = x == y
z.get()


Out[34]:
tensor([0])

In [35]:
z = x == y + 20
z.get()


Out[35]:
tensor([1])

Você também pode realizar operações de maximum.


In [36]:
x = torch.tensor([2, 3, 4, 1]).share(bob,alice, crypto_provider=crypto_provider)
x.max().get()


Out[36]:
tensor([4])

In [37]:
x = torch.tensor([[2, 3], [4, 1]]).share(bob,alice, crypto_provider=crypto_provider)
max_values, max_ids = x.max(dim=0)
max_values.get()


Out[37]:
tensor([4, 3])

Parabéns!!! - Hora de se juntar a comunidade!

Parabéns por concluir esta etapa do tutorial! Se você gostou e gostaria de se juntar ao movimento em direção à proteção de privacidade, propriedade descentralizada e geração, demanda em cadeia, de dados em IA, você pode fazê-lo das seguintes maneiras!

Dê-nos uma estrela em nosso repo do PySyft no GitHub

A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.

Junte-se ao Slack!

A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade! Você pode fazer isso preenchendo o formulário em http://slack.openmined.org

Contribua com o projeto!

A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de Issues (problemas) do PySyft no GitHub e filtrar por "Projetos". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos "independentes" pesquisando problemas no GitHub marcados como "good first issue".

Doar

Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!

Página do Open Collective do OpenMined


In [ ]: